package com.startupwebapp.dao.hibernate;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.LockMode;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Example;
import org.hibernate.criterion.Restrictions;
import org.springframework.beans.factory.annotation.Autowired;

import com.startupwebapp.dao.GenericDAO;

public abstract class GenericDAOSpringHibernateImpl<T, PK extends Serializable>
		implements GenericDAO<T, PK> {
	@Autowired
	protected SessionFactory sessionFactory;

	public Class<T> domainClass;

	@SuppressWarnings("unchecked")
	protected GenericDAOSpringHibernateImpl() {
		super();
		this.domainClass = (Class<T>) ((ParameterizedType) getClass()
				.getGenericSuperclass()).getActualTypeArguments()[0];

	}

	@SuppressWarnings("unchecked")
	public T findById(PK id) {
		return (T) getSession().get(domainClass, id);
	}

	@SuppressWarnings("unchecked")
	public PK save(T o) {
		return (PK) getSession().save(o);
	}

	public void update(T o) {
		getSession().update(o);
	}

	public void delete(T o) {
		getSession().delete(o);
	}

	protected Session getSession() {
		return this.sessionFactory.getCurrentSession();
	}

	@SuppressWarnings("unchecked")
	public List<T> findByProperty(String property, Object value) {
		DetachedCriteria criteria = DetachedCriteria.forClass(domainClass);
		criteria.add(Restrictions.eq(property, value));
		return criteria.getExecutableCriteria(getSession()).list();
	}

	@SuppressWarnings("unchecked")
	public T merge(T detachedInstance) {
		T result = (T) getSession().merge(detachedInstance);
		return result;
	}

	@SuppressWarnings("unchecked")
	public List<T> findByExample(T instance) {
		Example example = Example.create(instance);
		Criteria criteria = getSession().createCriteria(domainClass);
		criteria.add(example);
		List<T> results = criteria.list();
		return results;
	}

	public void attachDirty(T instance) {
		getSession().saveOrUpdate(instance);
	}

	public void attachClean(T instance) {
		getSession().lock(instance, LockMode.NONE);
	}

	@SuppressWarnings("unchecked")
	public List<T> findAll() {
		Criteria criteria = getSession().createCriteria(domainClass);
		return criteria.list();
	}

	public List<?> get(int first, int amount) {
		Criteria criteria = getSession().createCriteria(domainClass);
		criteria.setMaxResults(amount);
		criteria.setFirstResult(first);
		return criteria.list();
	}
}
